{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "EheA5_j_cEwc"
      },
      "source": [
        "##### Copyright 2019 Google LLC.\n",
        "\n",
        "Licensed under the Apache License, Version 2.0 (the \"License\");"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "YCriMWd-pRTP"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\"); { display-mode: \"form\" }\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "OvRwFTkqcp1e"
      },
      "source": [
        "# Monte Carlo Pricing in Tensorflow Quant Finance (TFF) using Euler Scheme\n",
        "\n",
        "\u003ctable class=\"tfo-notebook-buttons\" align=\"left\"\u003e\n",
        "  \u003ctd\u003e\n",
        "    \u003ca target=\"_blank\" href=\"https://colab.research.google.com/github/google/tf-quant-finance/blob/master/tf_quant_finance/examples/jupyter_notebooks/Monte_Carlo_Euler_Scheme.ipynb\"\u003e\u003cimg src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" /\u003eRun in Google Colab\u003c/a\u003e\n",
        "  \u003c/td\u003e\n",
        "  \u003ctd\u003e\n",
        "    \u003ca target=\"_blank\" href=\"https://github.com/google/tf-quant-finance/blob/master/tf_quant_finance/examples/jupyter_notebooks/Monte_Carlo_Euler_Scheme.ipynb\"\u003e\u003cimg src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" /\u003eView source on GitHub\u003c/a\u003e\n",
        "  \u003c/td\u003e\n",
        "\u003c/table\u003e"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "uG8UAZXjeUhf",
        "jupyter": {
          "outputs_hidden": true
        }
      },
      "outputs": [],
      "source": [
        "#@title Upgrade to TensorFlow 2.5+\n",
        "!pip install --upgrade tensorflow"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "QxMuce0vVKaL",
        "jupyter": {
          "outputs_hidden": true
        }
      },
      "outputs": [],
      "source": [
        "#@title Install TF Quant Finance\n",
        "!pip install tf-quant-finance"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "roUegEh5eh04",
        "jupyter": {
          "outputs_hidden": true
        }
      },
      "outputs": [],
      "source": [
        "#@title Install QuantLib\n",
        "!pip install QuantLib-Python"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "executionInfo": {
          "elapsed": 16,
          "status": "ok",
          "timestamp": 1629281741923,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "w3d7fZbDwivN",
        "outputId": "4218a7ea-0d0f-483b-c297-09a6c3f69b3b"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Wed Aug 18 10:15:41 2021       \n",
            "+-----------------------------------------------------------------------------+\n",
            "| NVIDIA-SMI 470.57.02    Driver Version: 460.32.03    CUDA Version: 11.2     |\n",
            "|-------------------------------+----------------------+----------------------+\n",
            "| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |\n",
            "| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |\n",
            "|                               |                      |               MIG M. |\n",
            "|===============================+======================+======================|\n",
            "|   0  Tesla T4            Off  | 00000000:00:04.0 Off |                    0 |\n",
            "| N/A   47C    P8    10W /  70W |      0MiB / 15109MiB |      0%      Default |\n",
            "|                               |                      |                  N/A |\n",
            "+-------------------------------+----------------------+----------------------+\n",
            "                                                                               \n",
            "+-----------------------------------------------------------------------------+\n",
            "| Processes:                                                                  |\n",
            "|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |\n",
            "|        ID   ID                                                   Usage      |\n",
            "|=============================================================================|\n",
            "|  No running processes found                                                 |\n",
            "+-----------------------------------------------------------------------------+\n"
          ]
        }
      ],
      "source": [
        "!nvidia-smi"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ku46LPe6Jswv"
      },
      "source": [
        "   Diffusion process $X(t) = (X_1(t), .. X_n(t))$ is a solution to a [Stochastic Differential Equation](https://en.wikipedia.org/wiki/Stochastic_differential_equation) (SDE)\n",
        "   $$dS_i = a_i(t, S) dt + \\sum_{j=1}^n S_{ij} (t, S) dW_j,\\ i \\in \\{1,.., n\\},$$\n",
        "\n",
        "where $n$ is the dimensionality of the diffusion, $\\{W_j\\}_{j=1}^n$ is $n$-dimensitonal Brownian motion, $a_i(t, S)$ is the instantaneous drift rate and the $S_{ij}(t)$ is the\n",
        "volatility matrix.\n",
        "\n",
        "In this colab we demonstrate how to draw samples from the difusion solution in TFF using the [Euler scheme](https://en.wikipedia.org/wiki/Euler%E2%80%93Maruyama_method). We also demonstrate how to use automatic differentiation framework for vega and delta computation and provide performance benchmarks.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "2nA2FSdTgcEM"
      },
      "outputs": [],
      "source": [
        "#@title Imports { display-mode: \"form\" }\n",
        "\n",
        "import matplotlib.pyplot as plt\n",
        "import numpy as np\n",
        "import time\n",
        "\n",
        "import tensorflow as tf\n",
        "\n",
        "import QuantLib as ql\n",
        "\n",
        " # tff for Tensorflow Finance\n",
        "import tf_quant_finance as tff\n",
        "\n",
        "from IPython.core.pylabtools import figsize\n",
        "figsize(21, 14) # better graph size for Colab  \n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "DaCP0d8mV_Yp"
      },
      "source": [
        "## Setting up the Euler sampling for European call option pricing under Black-Scholes\n",
        "\n",
        "The pricing function outputs prices on the grid `expiries x strikes`.\n",
        "\n",
        "Sampling is performed using `sample_paths` method of `GenericItoProcess` class. The user is free to provide their own drift and volatility function in order to sample from the desired process.\n",
        "\n",
        "For many processes there exist efficient specialized samplers and the user should feel free to explore the TFF [models module](https://github.com/google/tf-quant-finance/tree/master/tf_quant_finance/models).\n",
        "\n",
        "In this example, however, we consider only `GenericItoProcess`.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "WRYk-nfL_QQj"
      },
      "outputs": [],
      "source": [
        "#@title Set up parameters\n",
        "\n",
        "dtype = np.float64 #@param\n",
        "num_samples = 200000 #@param\n",
        "\n",
        "num_timesteps = 100 #@param\n",
        "\n",
        "expiries = [1.0] # This can be a rank 1 Tensor\n",
        "dt = 1. / num_timesteps\n",
        "rate = tf.constant(0.03, dtype=dtype)\n",
        "sigma = tf.constant(0.1, dtype=dtype)\n",
        "spot = tf.constant(700, dtype=dtype)\n",
        "\n",
        "strikes = tf.constant([600, 650, 680], dtype=dtype)\n",
        "\n",
        "def set_up_pricer(expiries, watch_params=False):\n",
        "    \"\"\"Set up European option pricing function under Black-Scholes model.\n",
        "    \n",
        "    Args:\n",
        "        expiries: List of expiries at which to to sample the trajectories.\n",
        "        watch_params: A Python bool. When `True`, gradients of the price function wrt the inputs\n",
        "          are computed more efficiently. \n",
        "    Returns:\n",
        "     A callable that accepts a rank 1 tensor of strikes, and scalar values for \n",
        "     the spots and  volatility values. The callable outputs prices of\n",
        "     the European call options on the grid `expiries x strikes`.\n",
        "    \"\"\"\n",
        "    def price_eu_options(strikes, spot, sigma):\n",
        "        # Define drift and volatility functions. \n",
        "        def drift_fn(t, x):\n",
        "          del t, x\n",
        "          return rate - 0.5 * sigma**2\n",
        "        def vol_fn(t, x):\n",
        "          del t, x\n",
        "          return tf.reshape(sigma, [1, 1])\n",
        "        # Use GenericItoProcess class to set up the Ito process\n",
        "        process = tff.models.GenericItoProcess(\n",
        "            dim=1,\n",
        "            drift_fn=drift_fn,\n",
        "            volatility_fn=vol_fn,\n",
        "            dtype=dtype)\n",
        "        log_spot = tf.math.log(tf.reduce_mean(spot))\n",
        "        if watch_params:\n",
        "            watch_params_list = [sigma]\n",
        "        else:\n",
        "            watch_params_list = None\n",
        "        paths = process.sample_paths(\n",
        "            expiries, num_samples=num_samples,\n",
        "            initial_state=log_spot, \n",
        "            watch_params=watch_params_list,\n",
        "            # Select a random number generator\n",
        "            random_type=tff.math.random.RandomType.PSEUDO_ANTITHETIC,\n",
        "            time_step=dt)\n",
        "        prices = (tf.exp(-tf.expand_dims(rate * expiries, axis=-1))\n",
        "                  * tf.reduce_mean(tf.nn.relu(tf.math.exp(paths) - strikes), 0))\n",
        "        return prices\n",
        "    return price_eu_options\n",
        "\n",
        "price_eu_options = tf.function(set_up_pricer(expiries))\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "executionInfo": {
          "elapsed": 8436,
          "status": "ok",
          "timestamp": 1629281826682,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "LAzzV5Da_QQm",
        "outputId": "eb01ab9d-ca9f-430a-a79d-0b1061e232be"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Time (seconds) to price a European Call Option on a CPU:  2.1619842052459717\n"
          ]
        }
      ],
      "source": [
        "#@title Pricing time a CPU. Note TensorFlow does automatic multithreading.\n",
        "\n",
        "# First run (includes graph optimization time)\n",
        "with tf.device(\"/cpu:0\"):\n",
        "    price_eu_options(strikes, spot, sigma)\n",
        "\n",
        "# Second run (excludes graph optimization time)\n",
        "time_start = time.time()\n",
        "with tf.device(\"/cpu:0\"):\n",
        "    prices = price_eu_options(strikes, spot, sigma)\n",
        "time_end = time.time()\n",
        "\n",
        "time_price_cpu = time_end - time_start\n",
        "print(\"Time (seconds) to price a European Call Option on a CPU: \", time_price_cpu)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "1AXfFFtYPdkK"
      },
      "source": [
        "### Better performance with XLA\n",
        "\n",
        "A quick guide on XLA is available on [TensorFlow website](https://www.tensorflow.org/xla).\n",
        "Roughly, XLA compiler generates a binary for the desired architecture (CPU/GPU/TPU). When underlying TF graph consists of\n",
        "many nodes, XLA might provide a significant speed up.\n",
        "\n",
        "Below we use `tf.function` with `jit_compile=True` to indicate that the whole Monte Carlo graph should be XLA-compiled."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "y65_Lj6jPdkK"
      },
      "outputs": [],
      "source": [
        "price_eu_options_xla = tf.function(set_up_pricer(expiries), jit_compile=True)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "executionInfo": {
          "elapsed": 2971,
          "status": "ok",
          "timestamp": 1629281898490,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "EwrwdkWhPdkN",
        "outputId": "6c25bd91-48b1-49ad-eed6-745896da63ab"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Time (seconds) to price a European Call Option on a CPU with XLA:  0.7147080898284912\n"
          ]
        }
      ],
      "source": [
        "#@title Pricing times on a CPU with XLA compilation\n",
        "\n",
        "# First run (includes graph optimization time)\n",
        "with tf.device(\"/cpu:0\"):\n",
        "    price_eu_options_xla(strikes, spot, sigma)\n",
        "\n",
        "# Second run (excludes graph optimization time)\n",
        "time_start = time.time()\n",
        "with tf.device(\"/cpu:0\"):\n",
        "    prices = price_eu_options_xla(strikes, spot, sigma)\n",
        "time_end = time.time()\n",
        "\n",
        "time_price_cpu_xla = time_end - time_start\n",
        "print(\"Time (seconds) to price a European Call Option on a CPU with XLA: \", time_price_cpu_xla)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "IXZnDEV-PdkR"
      },
      "source": [
        "### For the reference we provide performance of the Monte Carlo pricer in QuantLib"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "executionInfo": {
          "elapsed": 291,
          "status": "ok",
          "timestamp": 1629281918258,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "C1Rjiwe9_QQp",
        "outputId": "f0640bf9-1f1e-4fb9-c716-ca8467430bc6"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Time (seconds) to price a European Call Option using QuantLib:  11.273834705352783\n"
          ]
        }
      ],
      "source": [
        "#@title Monte Carlo sampling in QuantLib\n",
        "\n",
        "num_samples = 200000 #@param\n",
        "\n",
        "num_timesteps = 100 #@param\n",
        "\n",
        "expiry = 1.0\n",
        "\n",
        "calculation_date = ql.Date(1, 1, 2010)\n",
        "maturity_date = ql.Date(1, 1, 2011)\n",
        "day_count = ql.Thirty360(ql.Thirty360.BondBasis)\n",
        "calendar = ql.NullCalendar()\n",
        "\n",
        "ql_strike_price = 550\n",
        "sigma_ql = 0.1\n",
        "ql_volatility = ql.SimpleQuote(sigma_ql)\n",
        "ql_risk_free_rate = 0.03\n",
        "option_type = ql.Option.Call\n",
        "\n",
        "ql.Settings.instance().evaluationDate = calculation_date\n",
        "payoff = ql.PlainVanillaPayoff(option_type, ql_strike_price)\n",
        "\n",
        "eu_exercise = ql.EuropeanExercise(maturity_date)\n",
        "european_option_ql = ql.VanillaOption(payoff, eu_exercise)\n",
        "\n",
        "flat_ts = ql.YieldTermStructureHandle(\n",
        "    ql.FlatForward(calculation_date, ql_risk_free_rate, day_count)\n",
        ")\n",
        "flat_vol_ts = ql.BlackVolTermStructureHandle(\n",
        "    ql.BlackConstantVol(calculation_date, calendar,\n",
        "                        ql.QuoteHandle(ql_volatility), day_count)\n",
        ")\n",
        "\n",
        "spot_ql = 700\n",
        "spot_price = ql.SimpleQuote(spot_ql)\n",
        "spot_handle = ql.QuoteHandle(\n",
        "    spot_price\n",
        ")\n",
        "bsm_process = ql.BlackScholesProcess(spot_handle,\n",
        "                                      flat_ts,\n",
        "                                      flat_vol_ts)\n",
        "\n",
        "# Compute the same price number_of_options times\n",
        "\n",
        "engine = ql.MCEuropeanEngine(bsm_process, \"PseudoRandom\",\n",
        "                             timeSteps=num_timesteps,\n",
        "                             requiredSamples=num_samples,\n",
        "                             seed=42)\n",
        "\n",
        "european_option_ql.setPricingEngine(engine)\n",
        "# Price\n",
        "t = time.time()\n",
        "price_ql = european_option_ql.NPV()\n",
        "time_price_ql = time.time() - t\n",
        "print(\"Time (seconds) to price a European Call Option using QuantLib: \", time_price_ql)\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 553
        },
        "executionInfo": {
          "elapsed": 306,
          "status": "ok",
          "timestamp": 1629281929998,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "V2ms9TA7_QQr",
        "outputId": "21c55ab9-2a4d-43b9-b1ec-3cc1de24227c"
      },
      "outputs": [
        {
          "data": {
            "image/png": "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Yb2d03w//7iORyXf0Cbcffvtt+eQQw5Jktx6662ZNm1aZs7s3UBm3rx52XzzzddtnjWYNWtWttlmm0ybNi1Jcvrpp2eHHXbI4YcfnquuumqdXee3v/1tnvOc5+TrX/96pk2blmuvvTYnnnhirrvuumyzzTZ57GMfm/e///255pprcuSRR2b27Nm55557cvTRR+fkk0/OJz/5ycyfPz8f+MAHVp7z6U9/ek455ZTMmTMnhx56aM4555xst9126ywzAMC6tOGU5ik2Y8aMXHHFFUmSt771rdl6663zF3/xF1Ny7dZaWuuV+m984xvZfvvtV+678cYb1/n1PvGJT+Soo47KtGnTsmTJkhx22GE59dRT87znPS9JctFFF2Xx4sVJkrlz5+b888/PXXfdlX333XflMRN50YtelNNPPz1vfvOb13l2AIB1wfSMdWjBggV52tOelgMOOCC///u/n1tuuSVJb1T1TW96Uw466KA87nGPyyWXXJIkWbhwYQ466KDsu+++2WeffXLdddclSU499dTstdde2WuvvfLe9743Sa8M77bbbnnxi1+cvfbaKzfddFNnniVLluRlL3tZ9t577+y33375xje+kSQ57LDDcuWVVyZJ9ttvv7z97W9PkrzlLW/JRz/60fud58wzz8yRRx6ZJPnsZz+bgw8+eJUy/PSnPz177bXXKl+z1VZb5YADDsiPfvSjzpxHHHFEzjrrrM7jAACGRWleR1pree1rX5tzzz03CxYsyMtf/vJVRk6XLVuWefPm5b3vfW/e9ra3JUk+9KEP5XWve12uuOKKzJ8/PzvuuGMWLFiQf/7nf853vvOdXHbZZfnoRz+ayy+/PEly3XXX5TWveU0WLlyYnXfeOUnyjGc8I/vuu2+e+MQn3i/TBz/4wVRVvv/97+ess87KS17ykixZsiRz587NJZdckjvuuCObbrppvvWtbyVJLrnkkjz1qU9d5Rz33ntvrr/++syaNStJctVVV+WAAw7ofD1uv/32XHbZZdlzzz07j91uu+1yzz335Pbbb+88FgBgGEzPWEfuueeeXHXVVXnWs56VJLnvvvvyqEc9auX+o446KklywAEHrJxCcfDBB+ed73xnFi1alKOOOiq77rprLr300jz/+c/PVltttfLrLrnkkhxxxBHZeeed86QnPWmV664+PWOsSy+9NK997WuTJI9//OOz884759prr83cuXPzvve9L7Nnz85hhx2Wr33ta7n77rtzww03ZLfddlvlHLfddlse9rCHTfp1uOSSS7Lffvtlk002yUknnZQ999wz8+fPX+OxY1fHeMQjHpGf/exnmTFjxqSvBQAwVZTmdaS1lj333DP/9V//tcb9W2yxRZJk2rRpWbZsWZLk2GOPzROf+MR85StfyXOf+9x8+MMfnvAaK4r02jrwwAMzf/787LLLLnnWs56V2267LR/96EfXOIK85ZZbrrJW8p577pmLL7543HOvmNM81owZM/KrX/1qlW2//OUvVyn7S5YsyZZbbvlgnxIAwECZnrGObLHFFlm8ePHK0rx06dIsXLhwwq+5/vrrs8suu+SEE07IkUcemSuvvDJz587Nv/7rv+buu+/OXXfdlfPOOy9z5859UJnmzp2bM888M0ly7bXX5qc//Wl22223bL755tlpp51yzjnn5OCDD87cuXNzyimn3G9qRtKbOnHfffetLM7HHntsvv3tb+crX/nKymO++c1vTrhax4EHHphvfetbufXWW5Mk8+fPzz333JOddtopSe8XjltvvXXlFBAAgFGz4Yw0dywRN2ibbLJJzj333Jxwwgm54447smzZspx44okTzun9/Oc/nzPOOCObbbZZfud3fid//dd/nYc//OF56UtfmoMOOihJ8qd/+qfZb7/9HtSqGK95zWty3HHHZe+9986mm26aT37ykytHvOfOnZsLL7wwW265ZebOnZtFixaNW86f/exn59JLL82hhx6aLbfcMueff35OPPHEnHjiidlss82yzz775LTTTsttt922xq9/5CMfmdNOOy3Pfe5zs3z58my99dY566yzsskmvd/ZFixYkCc96UnZdNMN58cRANiw1Iqly0bZnDlz2urzYq+55prsvvvuQ0q0cfne976X97znPTnjjDMGcv7Xve51OeKII1auez2W7zMAMFWqakFrbc6a9pmeQaf9998/z3jGM3LfffcN5Px77bXXGgszAMCo8PdwJuXlL3/5wM79yle+cmDnBgBYF4w0AwBAB6UZAAA6mJ4BsIGaddJXug8CGEE3vvuwYUe4HyPNa2HRokU58sgjs+uuu2aXXXbJ8ccfn3vuuWedXuOiiy7Kt7/97ZWP3/rWt+aUU06533FPfvKTVx5/+OGHr9MMAAAbuw1mpHldj6h0/YbTWstRRx2V4447Ll/84hdz33335VWvelXe+MY35rTTTltnOS666KJsvfXWK0vxeMYWawAA1i0jzQ/S17/+9UyfPj0ve9nLkvRuj/2e97wnn/70p/OBD3wgxx9//MpjDz/88Fx00UVJkuOOOy5z5szJnnvumZNPPnnlMbNmzcrJJ5+c/fffP3vvvXd+8IMf5MYbb8yHPvShvOc978m+++6bSy65ZNw8W2+99crP77zzzhx22GHZbbfd8upXvzrLly9fx88eAGDjojQ/SAsXLswBBxywyrZtt902s2bNyrJly8b9une+852ZP39+rrzyylx88cW58sorV+7bfvvt873vfS/HHXdcTjnllMyaNSuvfvWr8+d//ue54oorJn077Xnz5uX9739/rr766vz4xz/OF77whQf3JAEASKI0T7nPf/7z2X///bPffvtl4cKFufrqq1fuO+qoo5IkBxxwwIO6bfYKBx10UHbZZZdMmzYtxxxzTC699NK1jQ0AsFFTmh+kPfbYIwsWLFhl25133plbb701M2bMWGVKxJIlS5IkN9xwQ0455ZRceOGFufLKK3PYYYet3JckW2yxRZLeVI+JRqu7VNWEjwEAeGCU5gfpkEMOyd13351Pf/rTSZL77rsvb3jDG3L88cdn9uzZueKKK7J8+fLcdNNNmTdvXpJeqd5qq63y0Ic+ND//+c9zwQUXdF5nm222yW9+85sHlG3evHm54YYbsnz58nzuc5/L7/3e7z3wJwgAwEpK84NUVTnvvPNy7rnnZtddd82MGTOyySab5M1vfnOe8pSnZPbs2dljjz1ywgknZP/990+SPOEJT8h+++2Xxz/+8Tn22GPzlKc8pfM6z3ve83Leeeet8kbAd7zjHdlxxx1XfqzuwAMPzPHHH5/dd989s2fPzvOf//x1++QBADYy1VobdoZOc+bMafPnz19l2zXXXJPdd999SInu79vf/naOOeaYnHfeeStLMmtv1L7PsD5xcxNgfTWsm5tU1YLW2pw17dtg1mketic/+cn5yU9+MuwYAAAMgOkZAADQQWkGAIAO63VpXh/mY/Pg+f4CAKNivS3N06dPz+23365YbaBaa7n99tszffr0YUcBAFh/3wi44447ZtGiRVm8ePGwozAg06dPX+OSegAAU229Lc2bbbZZZs+ePewYAABsBNbb6RkAADBVlGYA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\n",
            "text/plain": [
              "\u003cFigure size 864x648 with 1 Axes\u003e"
            ]
          },
          "metadata": {
            "needs_background": "light",
            "tags": []
          },
          "output_type": "display_data"
        }
      ],
      "source": [
        "#@title Plot the results\n",
        "\n",
        "ind = np.arange(1)  # the x locations for the groups\n",
        "width = 0.35  # the width of the bars\n",
        "\n",
        "fig, ax = plt.subplots()\n",
        "\n",
        "fig.set_figheight(9)\n",
        "fig.set_figwidth(12)\n",
        "\n",
        "ax.bar(ind - width/2, [time_price_cpu], width,\n",
        "       label='TensorFlow (CPU)', color='darkorange')\n",
        "ax.bar(ind + width/2, [time_price_ql], width,\n",
        "       label='QuantLib')\n",
        "\n",
        "# Add some text for labels, title and custom x-axis tick labels, etc.\n",
        "ax.set_ylabel('Time (sec) to sample {}'.format(num_samples))\n",
        "ax.set_title('Monte Carlo sampling comparison')\n",
        "ax.set_xticks(ind)\n",
        "ax.legend()\n",
        "\n",
        "\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "o6E1tQK5PdkX"
      },
      "source": [
        "# GPU vs CPU performace\n",
        "\n",
        "Above one can see that TF effectively applies multithreading to achieve the best possible CPU performance. If the user has access to a good GPU, additional boost can be achieved as demonstrated below.\n",
        "\n",
        "For this specific model XLA provides significant speedup for both CPU and GPU\n",
        "platforms.\n",
        "\n",
        "Tesla V100 provides 100x speed up compared to a cloud CPU with 8 logical cores\n",
        "(`n1-standard-8` machine type on Google Cloud Platform)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "9z78R5OtPdkX"
      },
      "outputs": [],
      "source": [
        "#@title Pricing times on CPU and GPU platforms\n",
        "\n",
        "# CPU without XLA\n",
        "with tf.device(\"/cpu:0\"):\n",
        "    price_eu_options(strikes, spot, sigma)\n",
        "time_start = time.time()\n",
        "with tf.device(\"/cpu:0\"):\n",
        "    prices = price_eu_options(strikes, spot, sigma)\n",
        "time_end = time.time()\n",
        "time_price_cpu = time_end - time_start\n",
        "\n",
        "# CPU with XLA\n",
        "with tf.device(\"/cpu:0\"):\n",
        "    price_eu_options_xla(strikes, spot, sigma)\n",
        "time_start = time.time()\n",
        "with tf.device(\"/cpu:0\"):\n",
        "    prices = price_eu_options_xla(strikes, spot, sigma)\n",
        "time_end = time.time()\n",
        "time_price_cpu_xla = time_end - time_start\n",
        "\n",
        "# GPU without XLA\n",
        "with tf.device(\"/gpu:0\"):\n",
        "    price_eu_options(strikes, spot, sigma)\n",
        "# Second run (excludes graph optimization time)\n",
        "time_start = time.time()\n",
        "with tf.device(\"/gpu:0\"):\n",
        "    prices = price_eu_options(strikes, spot, sigma)\n",
        "time_end = time.time()\n",
        "time_price_gpu = time_end - time_start\n",
        "\n",
        "# GPU with XLA\n",
        "with tf.device(\"/gpu:0\"):\n",
        "    price_eu_options_xla(strikes, spot, sigma)\n",
        "# Second run (excludes graph optimization time)\n",
        "time_start = time.time()\n",
        "with tf.device(\"/gpu:0\"):\n",
        "    prices = price_eu_options_xla(strikes, spot, sigma)\n",
        "time_end = time.time()\n",
        "time_price_gpu_xla = time_end - time_start\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 553
        },
        "executionInfo": {
          "elapsed": 299,
          "status": "ok",
          "timestamp": 1629281949567,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "1tzvzqRMPdka",
        "outputId": "c5435022-749c-489e-8747-848bd10e1ff7"
      },
      "outputs": [
        {
          "data": {
            "image/png": "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/OW23xs2bKBXr17V2+SipKSEyZMnc/nll1NZWcljjz1Go0aN+PKXv7zbfWfMmEG/fv14/PHHufjiiwHo0aMHq1atYsWKFRxzzDF7cOW7sp1DkiSpgVVWVnLKKafQp08fzjjjDN5//30gM+p67bXX0r9/f7p27Up5eTkAixYton///vTq1YuePXuydOlSACZOnEhpaSmlpaVMmjQJyITjbt26ccEFF1BaWsrKlSt3W09VVRUXX3wxPXr0oHfv3syePRuAYcOGMX/+fAB69+7NzTffDMANN9zAlClTdjnO9OnTGTFiBAA//vGPGThw4A7h+NRTT6W0tHSHfQ488ED69OnDO++8k/sLCAwYMICBAwdy0003MW7cuB1Gr+vym9/8hj//+c9873vfY8aMGTusO/vss3n44YfzqqE+hmhJkqQGlFJi9OjRPPbYY1RWVnLJJZdw/fXXV6/funUrc+fOZdKkSYwfPx6Ae++9l6uvvpp58+ZRUVHB0UcfTWVlJQ8++CBvvPEGr7/+OlOmTOGtt94CYOnSpVx55ZUsWrSIDh06ADBo0CB69erFgAEDdqnpnnvuISJYsGABM2bM4MILL6SqqoqysjLKy8tZv349jRs35tVXXwWgvLy8uo1iu82bN7Ns2TI6duwIwMKFC+nTp89uX49169bx+uuv13vb87rceuutTJo0iVGjRnHcccftdvuHH36Y888/n7KyMn7961/zwQcfVK/r27dv9YeWhmA7hyRJUgPatGkTCxcuZPDgwUCmv/eII46oXj9y5EgA+vTpU91yMXDgQG655RZWrVrFyJEj6dKlC3PmzOGcc87hwAMPrN6vvLyc4cOH06FDB0466aQdzrtzO0dNc+bMYfTo0QAcf/zxdOjQgSVLllBWVsadd95Jp06dGDZsGM8//zwbN27k3XffpVu3bjscY+3atbRq1Srn16G8vJzevXvTqFEjrrvuOrp3705FRUWt20ZErctfeeUVDj744Jz7umfMmMGsWbNo1KgR//iP/8jMmTO56qqrADj00EN57733cq5/dwzRkiRJDSilRPfu3XnttddqXd+sWTMg0/e7detWAEaNGsWAAQN45plnGDp0KPfdd1+959gerD+tfv36UVFRQefOnRk8eDBr165lypQptY4wN2/enKqqqurn3bt35+WXX67z2Nt7omtq27Ytf/jDH3ZY9uGHH9Ya/jds2MB3vvMdXnzxRS6++GKeffZZhg4dWuf5FixYwNKlS6s/vGzevJlOnTpVh+iqqiqaN29e5/75sp1DkiSpATVr1ow1a9ZUh+gtW7awaNGievdZtmwZnTt3ZsyYMYwYMYL58+dTVlbGE088wcaNG9mwYQOzZs2irKxsj2oqKytj+vTpACxZsoQVK1bQrVs3mjZtSvv27Zk5cyYDBw6krKyMCRMm7NLKAdC6dWu2bdtWHaRHjRrFL37xC5555i/30XjllVfqHTXu168fr776KqtXrwagoqKCTZs20b59+122vfnmmzn33HM5/vjjmTx5Mtdcc80OIX5nM2bM4KabbmL58uUsX76c9957j/fee4/f/va31de9c7/2p+FItCRJ+uuymynpCq1Ro0Y89thjjBkzhvXr17N161bGjh1bb0/wo48+yrRp02jSpAmHH34448aNo02bNlx00UX0798fgMsuu4zevXvv0awbV155JVdccQU9evSgcePGTJ06tXpEvKysjBdeeIHmzZtTVlbGqlWr6gzrQ4YMYc6cOZx++uk0b96cp59+mrFjxzJ27FiaNGlCz549ueOOO1i7dm2t+x922GHccccdDB06lE8++YQWLVowY8YMGjXacVx30aJFzJo1i//93/8FMl96POOMM/iP//gPbrzxRgB69uxZvd+5557LE088wbPPPrvDcc455xwefvhhrr32WmbPns2wYQ13Y7PYPi3K/qJv376prn6aQvOOhSok71goSXtm8eLFnHDCCfu6jM+EN998k9tvv51p06bt61LysmnTJk455RTmzJlD48a1jyHX9j6KiMqUUt/atredQ5IkSTk58cQTGTRoENu2bdvXpeRlxYoV3HbbbXUG6D1hO4ckSZJydskll+zrEvLWpUsXunTp0qDHdCRakiRJypMhWpIkScqTIVqSJEnKkyFakiRJypNfLJQkSX9VejzUo0GPt+DCBfWuX7duHaeddhoAq1evpqSkhHbt2gEwd+5cmjZt2qD11KZjx460bNmSkpISACZPnsyRRx7JWWedlfMts3Px8ccfc+aZZ/Liiy9SUlLC0qVLueaaa1i8eDGtWrXioIMOYvz48Zx88slMnTqVb3/72xx11FFs3ryZa665hssvv5ybbrqJFi1a8K1vfWuH+isqKjjooIM4/fTTefHFFxt0Jo1CKO7qJEmSilzbtm2ZN28eQK0BsZBSSmy/58fs2bN3uH32ntyUZXceeOABRo4cSUlJCVVVVQwbNowJEyYwfPhwABYuXEhFRUX1HQ/PO+887r77bn7/+9/TvXv36u3q0rRpU0477TQeeeQRvvrVrzZ4/Q3Jdg5JkqQGVllZySmnnEKfPn0444wzeP/99wE49dRTufbaa+nfvz9du3alvLwcyNyhr3///vTq1YuePXuydOlSACZOnEhpaSmlpaVMmjQJyITjbt26ccEFF1BaWsrKlSt3W09VVRUXX3wxPXr0oHfv3syePRuAYcOGMX/+fCBzV8Cbb74ZgBtuuIEpU6bscpzp06czYsSI6p8HDhy4QzAuLS3loosu2mW/Qw89lGOPPbb6Ftz1+eIXv1h9i/Ji5ki0JElSA0opMXr0aJ588knatWvHI488wvXXX88DDzwAwNatW5k7dy7PPvss48eP5+c//zn33nsvV199NV/96lfZvHkz27Zto7KykgcffJA33niDlBIDBgzglFNOoXXr1ixdupSHHnqIk046qfq8gwYNoqSkhGbNmvHGG2/sUNM999xDRLBgwQLefvtthgwZwpIlSygrK6O8vJwOHTrQuHFjXn31VQDKy8u59957dzjG5s2bWbZsGR07dgQywf/EE0/M6TVZtmwZy5Yt47jjjtvttqWlpfzyl7/M6bj7kiFakiSpAW3atImFCxcyePBgALZt28YRRxxRvX7kyJEA9OnTp7rlYuDAgdxyyy2sWrWKkSNH0qVLF+bMmcM555zDgQceWL1feXk5w4cPp0OHDjsEaNi1naOmOXPmMHr0aACOP/54OnToUB2i77zzTjp16sSwYcN4/vnn2bhxI++++y7dunXb4Rhr166lVatWdV73Oeecw9KlS+natSuPP/44AI888ghz5syhWbNm3HfffbRp04aIqHX/7ctLSkpo2rQpH330ES1btqzzfPuaIVqSJKkBpZTo3r07r732Wq3rmzVrBmTC4tatWwEYNWoUAwYM4JlnnmHo0KHcd9999Z5je7D+tPr160dFRQWdO3dm8ODBrF27lilTptCnT59dtm3evDlVVVXVz7t3784rr7xS/XzWrFlUVFTs0A++vSe6prZt21a3t2z30Ucf7RDQN23axAEHHPCpr6+Q7ImWJElqQM2aNWPNmjXVIXrLli0sWrSo3n2WLVtG586dGTNmDCNGjGD+/PmUlZXxxBNPsHHjRjZs2MCsWbMoKyvbo5rKysqq+4yXLFnCihUr6NatG02bNqV9+/bMnDmTgQMHUlZWxoQJE6q/GFhT69at2bZtW3WQHjVqFK+++ipPPfVU9TYbN27cbS0nn3wyTz31FB999BEAjz/+OJ/73OeqZxZZt24dhxxyCE2aNNmja91bHImWJEl/VXY3JV2hNWrUiMcee4wxY8awfv16tm7dytixY+nevXud+zz66KNMmzaNJk2acPjhhzNu3DjatGnDRRddRP/+/QG47LLL6N279x7NunHllVdyxRVX0KNHDxo3bszUqVOrR8TLysp44YUXaN68OWVlZaxatarOsD5kyBDmzJnD6aefTvPmzXn66af55je/ydixYznssMNo2bIl3/3ud+utpWfPnlx11VV84QtfICI49NBDuf/++6vXz549m2HDhuV9jXtbbJ8WZX/Rt2/fVFFRsU/O3fG6Z/bJefXZsPy24v8PQ5KK0eLFiznhhBP2dRmfCW+++Sa3334706ZNK9g5Ro4cyW233UbXrl0Ldo7a1PY+iojKlFLf2ra3nUOSJEk5OfHEExk0aBDbtm0ryPE3b97MF7/4xb0eoPeE7RySJEnK2SWXXFKwYzdt2pQLLrigYMdvSI5ES5IkSXkyREuSJEl5KliIjoj2ETE7In4VEYsi4upatomIuDMi3omI+RGR221vJEmSpH2okD3RW4F/TSm9GREtgcqIeD6l9Ksa2/wD0CX7GAD8V/ZfSZIkqWgVLESnlN4H3s/+/FFELAaOAmqG6BHAj1Jmnr3XI6JVRByR3VeSJClvi49v2OnuTnh7cb3r161bx2mnnQbA6tWrKSkpoV27dgDMnTuXpk2bNmg9tenYsSMtW7asvmHJ5MmTOfLIIznrrLNYuHBhg53n448/5swzz+TFF1+kpKSEpUuXcs0117B48WJatWrFQQcdxPjx4zn55JOZOnUq3/72tznqqKPYvHkz11xzDZdffjk33XQTLVq02OHOhh07dqSiomKH25Z/9NFH9OrVi+eee44uXbqwZcsWTjzxRO6//34GDBhAixYt+POf/1xrnWPHjmXmzJmsXLmSRo0yjRdPP/00c+fO5eabb26Q12Kv9ERHREegN/DGTquOAlbWeL4qu2zn/b8eERURUbFmzZpClSlJkpS3tm3bMm/ePObNm8e//Mu/cM0111Q/L3SATinxySefAJmblGw/7+c///mCnO+BBx5g5MiRlJSUUFVVxbBhw/j617/Ob37zGyorK7nrrrtYtmxZ9fbnnXce8+bN46WXXmLcuHF88MEHOZ+rZcuW3HrrrVx11VUATJgwgc9//vMMGFB/08Inn3zCrFmzaN++PS+//HL18mHDhvGTn/wkp7sq5qLgIToiWgD/DxibUvrTnhwjpfSDlFLflFLf7Z/sJEmSilVlZSWnnHIKffr04YwzzuD99zN/ZD/11FO59tpr6d+/P127dqW8vByARYsW0XfdsPIAAB6USURBVL9/f3r16kXPnj1ZunQpABMnTqS0tJTS0lImTZoEwPLly+nWrRsXXHABpaWlrFy5svYiaqiqquLiiy+mR48e9O7dm9mzZwOZYDl//nwAevfuXT1Ke8MNNzBlypRdjjN9+nRGjBhR/fPAgQMZPnx49frS0lIuuuiiXfY79NBDOfbYY/ntb3+b0+u33bnnngvAf/7nf3Lvvfdy66237nafl156ie7du3PFFVcwY8aM6uURwamnnsrTTz+dVw11Keg80RHRhEyAnp5SeryWTX4HtK/x/OjsMkmSpP1SSonRo0fz5JNP0q5dOx555BGuv/56HnjgAQC2bt3K3LlzefbZZxk/fjw///nPuffee7n66qv56le/yubNm9m2bRuVlZU8+OCDvPHGG6SUGDBgAKeccgqtW7dm6dKlPPTQQ5x00knV5x00aBAlJSU0a9aMN97Y8Y//99xzDxHBggULePvttxkyZAhLliyhrKyM8vJyOnToQOPGjXn11VcBKC8v5957793hGJs3b2bZsmV07NgRyAT/E0/MbU6IZcuWsWzZMo477ri8X8877riDE044gR/84Ae0adNmt9vPmDGDr3zlK4wYMYJx48axZcsWmjRpAkDfvn0pLy+vDuefRsFCdEQE8ENgcUppYh2bPQVcFREPk/lC4Xr7oSVJ0v5s06ZNLFy4kMGDBwOwbds2jjjiiOr1I0eOBKBPnz4sX74cgIEDB3LLLbewatUqRo4cSZcuXZgzZw7nnHMOBx54YPV+5eXlDB8+nA4dOuwQoCHTzlGzp7imOXPmMHr0aACOP/54OnToUB2i77zzTjp16sSwYcN4/vnn2bhxI++++y7dunXb4Rhr166lVatWdV73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\n",
            "text/plain": [
              "\u003cFigure size 864x648 with 1 Axes\u003e"
            ]
          },
          "metadata": {
            "needs_background": "light",
            "tags": []
          },
          "output_type": "display_data"
        }
      ],
      "source": [
        "#@title Plot the results\n",
        "\n",
        "ind = np.arange(1)  # the x locations for the groups\n",
        "width = 0.35  # the width of the bars\n",
        "\n",
        "fig, ax = plt.subplots()\n",
        "\n",
        "fig.set_figheight(9)\n",
        "fig.set_figwidth(12)\n",
        "\n",
        "ax.bar(ind - width/8, [time_price_cpu], width / 8,\n",
        "       label='TensorFlow (CPU)')\n",
        "ax.bar(ind, [time_price_cpu_xla], width / 8,\n",
        "       label='TensorFlow (CPU XLA)')\n",
        "ax.bar(ind + width/8, [time_price_gpu], width / 8,\n",
        "       label='TensorFlow (GPU)')\n",
        "ax.bar(ind + width/4, [time_price_gpu_xla], width / 8,\n",
        "       label='TensorFlow (GPU XLA)')\n",
        "\n",
        "# Add some text for labels, title and custom x-axis tick labels, etc.\n",
        "ax.set_ylabel('Time (sec) to sample {}'.format(num_samples))\n",
        "ax.set_title('Monte Carlo sampling comparison')\n",
        "ax.set_xticks(ind)\n",
        "ax.legend()\n",
        "\n",
        "\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "NpdgpaosPdkc"
      },
      "source": [
        "# Greek computation \n",
        "\n",
        "TF provides framework for automatic differentiation. Below we demostrate how to compute vega and delta for the European call options and compare the estimated results ti the true values."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "YDmea-ACPdkd"
      },
      "outputs": [],
      "source": [
        "# Set up the pricer\n",
        "expiries = [0.5, 1.0]\n",
        "strikes = tf.constant([600, 650, 680], dtype=dtype)\n",
        "sigma = tf.constant(0.1, dtype=dtype)\n",
        "price_eu_options = set_up_pricer(expiries, watch_params=True)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "r1WQJM-KPdkf"
      },
      "outputs": [],
      "source": [
        "price_eu_options_no_watched_params = set_up_pricer(expiries)\n",
        "@tf.function(jit_compile=True)\n",
        "def price_eu_options_xla(strikes, spot, sigma):\n",
        "    return price_eu_options_no_watched_params(strikes, spot, sigma)\n",
        "\n",
        "@tf.function\n",
        "def vega_fn(sigma):\n",
        "    fn = lambda sigma: price_eu_options(strikes, spot, sigma)\n",
        "    return tff.math.fwd_gradient(fn, sigma,\n",
        "                                 use_gradient_tape=True)\n",
        "\n",
        "@tf.function(jit_compile=True)\n",
        "def vega_fn_xla(sigma):\n",
        "    return vega_fn(sigma)\n",
        "\n",
        "@tf.function\n",
        "def delta_fn(spot):\n",
        "    fn = lambda spot: price_eu_options(strikes, spot, sigma)\n",
        "    return tff.math.fwd_gradient(fn, spot,\n",
        "                                 use_gradient_tape=True)\n",
        "\n",
        "@tf.function(jit_compile=True)\n",
        "def delta_fn_xla(spot):\n",
        "    return delta_fn(spot)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "executionInfo": {
          "elapsed": 1305,
          "status": "ok",
          "timestamp": 1629282288180,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "JfUgriuUPdkh",
        "outputId": "1f4954de-d9bc-4ef4-cae6-fcc07cb0a1f3"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Estimated deltas on grid expiries x strikes: \n",
            " [[0.99248277 0.90309587 0.74544609]\n",
            " [0.97055601 0.86385565 0.73988398]]\n"
          ]
        }
      ],
      "source": [
        "estimated_deltas = delta_fn_xla(spot)\n",
        "print(\"Estimated deltas on grid expiries x strikes: \\n\", estimated_deltas.numpy())"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "executionInfo": {
          "elapsed": 1479,
          "status": "ok",
          "timestamp": 1629282291509,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "2TnariSOPdkk",
        "outputId": "48d871bc-7466-4206-c635-c3f2710f3ca4"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Estimated vegas on grid expiries x strikes: \n",
            " [[  9.56612853  83.84885948 158.35179387]\n",
            " [ 46.27546525 153.31475986 226.91236681]]\n"
          ]
        }
      ],
      "source": [
        "estimated_vegas = vega_fn_xla(sigma)\n",
        "print(\"Estimated vegas on grid expiries x strikes: \\n\", estimated_vegas.numpy())"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "8Ke8hsu7Pdkm"
      },
      "outputs": [],
      "source": [
        "expiries_tensor = tf.expand_dims(tf.convert_to_tensor(expiries, dtype=dtype), axis=1)\n",
        "true_delta_fn = lambda spot : tff.black_scholes.option_price(volatilities=sigma,\n",
        "                                                             strikes=strikes,\n",
        "                                                             spots=spot,\n",
        "                                                             expiries=expiries_tensor,\n",
        "                                                             discount_rates=rate)\n",
        "true_vega_fn = lambda sigma : tff.black_scholes.option_price(volatilities=sigma,\n",
        "                               strikes=strikes,\n",
        "                               spots=spot,\n",
        "                               expiries=expiries_tensor,\n",
        "                               discount_rates=rate)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "executionInfo": {
          "elapsed": 9,
          "status": "ok",
          "timestamp": 1629282296017,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "2TNBEHxZPdko",
        "outputId": "527bfa80-c0b0-4d8f-827a-e15508d620bd"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "True deltas on grid expiries x strikes: \n",
            " [[0.99239851 0.90243168 0.74454875]\n",
            " [0.97072164 0.8623811  0.73887319]]\n",
            "True vegas on grid expiries x strikes: \n",
            " [[ 10.37265092  85.31635365 159.08825372]\n",
            " [ 46.67659297 153.994105   227.5617336 ]]\n"
          ]
        }
      ],
      "source": [
        "true_delta = tff.math.fwd_gradient(true_delta_fn, spot)\n",
        "true_vega = tff.math.fwd_gradient(true_vega_fn, sigma)\n",
        "print(\"True deltas on grid expiries x strikes: \\n\", true_delta.numpy())\n",
        "print(\"True vegas on grid expiries x strikes: \\n\", true_vega.numpy())"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "executionInfo": {
          "elapsed": 245,
          "status": "ok",
          "timestamp": 1629282298480,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "qKTyD12zPdkq",
        "outputId": "96e300ce-7d7f-485d-8ff1-0df07fe84e34"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Relative error in delta estimation: \n",
            " 0.0017098568942449592\n",
            "Relative error in vega estimation: \n",
            " 0.0777547026302402\n"
          ]
        }
      ],
      "source": [
        "print(\"Relative error in delta estimation: \\n\", np.max(abs(estimated_deltas - true_delta) / true_delta))\n",
        "print(\"Relative error in vega estimation: \\n\", np.max(abs(estimated_vegas - true_vega) / true_vega))\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "VAq-N-F-Pdks"
      },
      "outputs": [],
      "source": [
        "#@title Greek computation speed\n",
        "\n",
        "# Price CPU with XLA\n",
        "## warmup\n",
        "with tf.device(\"/cpu:0\"):\n",
        "    price_eu_options_xla(strikes, spot, sigma)\n",
        "## measure time\n",
        "time_start = time.time()\n",
        "with tf.device(\"/cpu:0\"):\n",
        "    prices = price_eu_options_xla(strikes, spot, sigma)\n",
        "time_end = time.time()\n",
        "time_price_cpu = time_end - time_start\n",
        "\n",
        "# Delta CPU with XLA\n",
        "## warmup\n",
        "with tf.device(\"/cpu:0\"):\n",
        "    delta_fn_xla(spot)\n",
        "## measure time\n",
        "time_start = time.time()\n",
        "with tf.device(\"/cpu:0\"):\n",
        "    delta_fn_xla(spot)\n",
        "time_end = time.time()\n",
        "time_delta_cpu = time_end - time_start\n",
        "\n",
        "# Vega CPU with XLA\n",
        "## warmup\n",
        "with tf.device(\"/cpu:0\"):\n",
        "    vega_fn_xla(spot)\n",
        "## measure time\n",
        "time_start = time.time()\n",
        "with tf.device(\"/cpu:0\"):\n",
        "    vega_fn_xla(spot)\n",
        "time_end = time.time()\n",
        "time_vega_cpu = time_end - time_start\n",
        "\n",
        "# Price GPU with XLA\n",
        "## warmup\n",
        "with tf.device(\"/gpu:0\"):\n",
        "    price_eu_options_xla(strikes, spot, sigma)\n",
        "## measure time\n",
        "time_start = time.time()\n",
        "with tf.device(\"/gpu:0\"):\n",
        "    prices = price_eu_options_xla(strikes, spot, sigma)\n",
        "time_end = time.time()\n",
        "time_price_gpu = time_end - time_start\n",
        "\n",
        "# Delta GPU with XLA\n",
        "## warmup\n",
        "with tf.device(\"/gpu:0\"):\n",
        "    delta_fn_xla(spot)\n",
        "## measure time\n",
        "time_start = time.time()\n",
        "with tf.device(\"/gpu:0\"):\n",
        "    delta_fn_xla(spot)\n",
        "time_end = time.time()\n",
        "time_delta_gpu = time_end - time_start\n",
        "\n",
        "# Vega GPU with XLA\n",
        "## warmup\n",
        "with tf.device(\"/gpu:0\"):\n",
        "    vega_fn_xla(spot)\n",
        "## measure time\n",
        "time_start = time.time()\n",
        "with tf.device(\"/gpu:0\"):\n",
        "    vega_fn_xla(spot)\n",
        "time_end = time.time()\n",
        "time_vega_gpu = time_end - time_start"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 553
        },
        "executionInfo": {
          "elapsed": 261,
          "status": "ok",
          "timestamp": 1629282337416,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "VPAWot3lPdku",
        "outputId": "6058d5fd-2857-4a7f-d1bd-f0fdbac052eb"
      },
      "outputs": [
        {
          "data": {
            "image/png": "iVBORw0KGgoAAAANSUhEUgAAAtAAAAIYCAYAAABE7NtFAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAgAElEQVR4nO3de7hdVXkv/u+bkEg0gD+5nKOgBBSRSyBAoAWKJlUkXArHc9AjqMSqVanir0hFqEUROYqUeilaES2iiIJFqVzrDWkRVEgwKIgg0igIlYs0gBJJ4jh/rJWcnZ2dZE/JYofw+TzPfthzzjHHfOdaKw/fNfaYc1ZrLQAAwOiMG+sCAADgiUSABgCADgRoAADoQIAGAIAOBGgAAOhAgAYAgA4EaOBJparmV9VLxrqOrqpqSlW1qlpvrGtZ21XVc6rq4aoaP9a1AOsmARqepPpB8tGq2mTY+h/0g9qUNXCMK6vqDY+xj/2q6t+r6qGqureq/q2qDn6sta0JVfXKqvp+Vf2mqu7p//6XVVVjXdsT1R/yRWH4l6LW2i9aa5Nba0sGU+WaUVUbVtVHquoX/cD/s/7yJv3t86vqkf62X1XV2VU1eci2lwzr77VV9Z2xOBd4shGg4cntP5IctnShqqYmeerYlbO8qjo0yT8n+VySLZL8tyTvTvJnf0Bfa3TktqqOSfLRJH+X5L/3a3tzkr2TTFzJPkZESZJU1cQk30qyQ5JZSTZMsmeS+5PsMaTpn7XWJifZNcn0JH/7OJcKjECAhie3c5IcMWR5dnphdZmq2qiqPtcf/f15Vf1tVY3rb3ttVX2nqk6rqgeq6j+qav/+tv+TZJ8kH+uPoH2sv/4FVfWNqvp1Vd1SVa8YqbD+KO6Hkryvtfbp1tqC1trvW2v/1lr7i36b51bVFVV1f1XdV1XnVtXTh/Qxv6reWVU/TPKb4SG6qp7SH/G7q//zkap6yupetKraKMlJSf6ytXZBa+2h1vOD1tqrWmu/67c7u6o+UVWXVdVvksysqmdV1Zf7r+d/VNXbhvQ7rqqO649E3l9VX6qqZ6ykhv/VP78dq2r9qvp8f5//qqrrquq/rWS/Z1fVV/rHv3/I+zKu/97+vD+a/rn+eQ4dFf7zqrqj/16/uap2r6of9o/5sSHHeG1VXV1VH6uqBVX1k6p68bD35SVDlk+sqs/3F/+9/9//6n9u9lzV+1xV5yR5TpKL++2PrWGj2P3X/KL+Z+62qvqLYcf+Uv98H6qqm6pq+ire+736r++C/n/3GrLtyqp6X//cH6qqr9ewv/AMcUS/7pe11n7c/2zf01p7X2vtsuGNW2u/THJ5kh1XVhvw+BGg4cnte0k2rKrtqjc6+soknx/W5vQkGyXZOsmL0vsf/58P2f5HSW5JskmSU5P8U1VVa+1dSa5K8tb+n9PfWlVPS/KNJF9Isln/eP9YVduPUNu2SZ6d5IJV1F9JPpDkWUm267c/cVibw5IcmOTprbXFw7a9K8kfJ5mWZOf0Rv5GM8K3Z5KnJPnqKNoenuT/JNkgyTVJLk5yQ5LNk7w4yV9V1X79tkcl+R/pvc7PSvJAko8P77Cq/jzJB5O8pLV2Y3pffDZK7/w3Tm8k/JER9huf5JIkP08ypV/Def3Nr+3/zEzvvZ6c5GPDuvijJNsk+d9JPpLe6/eS9EZRX1FVLxrW9mfpfS7ek+QrK/syMMwL+/99ev9z892s4n1urb0myS/SH6ltrZ06Qp/nJbmzv/+hSd5fVX86ZPvB/TZPT3LRCOedJOnXf2mSf0jvdf5QkkurauMhzQ5P79/HZun9JeKvV3KeL0nyr621h1eyffixn53kgCQ/GE17YLAEaGDpKPS+SW5O8sulG4aE6uP7o6zzk/x9ktcM2f/nrbVP9eebfjbJM9ObzjCSg5LMb619prW2uLX2gyRfTvLyEdouDSV3r6zw1tptrbVvtNZ+11q7N71A86Jhzf6htXZHa22FQJnkVUlO6o/83ZvkvcPObWU2SXLf0EBeVdf0R2IfqaoXDmn71dba1a213yeZmmTT1tpJrbVHW2u3J/lUeq9x0gu+72qt3dkfxT4xyaHDRs7/Ksk7ksxord3WX7covdfrea21Ja21ua21B0eoe4/0QuQ7Wmu/aa0tbK0tnTP7qiQfaq3d3g91xyd55bBjv6+/z9eT/CbJF/uv3S/T+7K0y5C29yT5SGttUWvt/PS+ZB24mtd1RKN8n0fUD557J3lnv/Z5ST6d5f/y8p3W2mX9z/A56X2ZGsmBSX7aWjun//n9YpKfZPkpRZ9prd3a/7x9Kb0vZyPZOKv4bA/xL1X1X0m+k+Tfkrx/FPsAA+ZqbuCc9P5svlWGTd9ILyhOSG/EcqmfpzdyudR/Lv2ltfbb6l0/N3klx9oyyR/1A8FS6/VrGO7+/n+fmd5c7RX0pyl8NL2pIhukNyjwwLBmd6yklqQXJoef27NW0X5obZtU1XpLQ3Rrba9+TXdm+cGJocffMsmzhp3/+PTC59LtF1bV74dsX5Llv5C8I73Qf+eQdeekNyp7Xn9qw+fTC+KLhtX97PS+8AwfiU9Gfi3WG3bsXw35/ZERloe+779srbVh/Y3mtV3BKN/nlXlWkl+31h4aVsvQaRr/OeT33yZZf+h7O6yvnw9bt9J/D/2+VvZv4f70Ptur8z9aa98cYf3i9P5tDjUhvS9TwIAZgYYnudbaz9MLqAck+cqwzfel9z/kLYese06GjFKvrvthy3ck+bfW2tOH/ExurR05wr639Nv/r1X0//7+Maa21jZM8ur0/ty/qhqGuisrnttdq2i/1HeT/C7JIaNoO/T4dyT5j2Hnv0Fr7YAh2/cftn39/gjvUi9N8rdVtex16Y/yvre1tn2SvdIb6R86wjr0+M+pkS+oHOm1WJzlQ3IXm1ctdzeSoa/tb7L8xar/fcjvI71fq3ufV/ceP6OqNhhWy2g/w8P72nLYuj+0r28m2a8/rekP8Yv0puEMtVVWDPjAAAjQQJK8PsmfttZ+M3Rl/0/aX0ryf6pqg6raMsnbs+I86ZX5VXrzaZe6JMnzq+o1VTWh/7N7VW03fMf+6OXbk5zQv3htw/6Fbn9SVWf2m22Q5OEkC6pq8/RGZ7v4YnphdNP+xV7vHs25tdb+K73pHv9YVYf2X5txVTUtyaoC0bVJHqrehY2Tqmp89S4C3L2//Yz0Xustk6Rf1/CQflN6d234ePVv51dVM6tqan/KzYPpfen5fVZ0bXrTBk6pqqdV7+LDvYe8FkdX1VbVu1Xa+5Ocv5LR6tHYLMnb+u/xy9Obu7z04rh56U0PmdC/YO/QIfvd26996Odmde/z8M/ZMq21O9Kbe/6B/vnulN7nfbSf4aEuS+/ze3hVrVdV/zvJ9ul9rrs6J70vNF+u3oW146pq46r6m6o6YHU7Jzk/vfnzL6ie6Ulel/83px0YIAEaSGvtZ621OSvZfFR6I4a3pzcP8wtJzhpl1x9Nbw7vA1X1D/0/o780vTm/d6X35+4PpndB3kh1XZDeBWuv67f/VZKT8/8u3ntverf3WpDexV3DR9BX5+Qkc5L8MMmPklzfX5eqelVV3bSyHfsXq709ybH9un6V5JNJ3pleYBtpnyXpjQ5PS2/U/7705uNu1G/y0fQuYvt6VT2U3kWefzRCPzf0+/lU9e568t/Tu9jywfTmsf9bRpgW0z/+nyV5XnojmHem9/omvfd06XSe/0iyML33/g/1/fQuOLwvvYsoD22tLZ2Wc0KS56Y3DeO96X2mltb42377q/tzyv84q3+fP5DeF6H/qqqRLto7LL3R2ruSXJjkPSuZFrFK/foPSnJMelMwjk1yUGvtvj+gr9+ldyHhT9K7sPbB9L7gbJLea7c6n0rymfQuSl2Q3vSrd7XW/rVrLUB3tfwUNQB4bKrqtUne0Fr7k7GuBWAQjEADAEAHAjQAAHRgCgcAAHRgBBoAADoQoAEAoIMn3JMIN9lkkzZlypSxLgMAgHXc3Llz72utbTp8/RMuQE+ZMiVz5qzsdrUAALBmVNWIT/c0hQMAADoQoAEAoAMBGgAAOnjCzYEeyaJFi3LnnXdm4cKFY10Ka9j666+fLbbYIhMmTBjrUgAAkqwjAfrOO+/MBhtskClTpqSqxroc1pDWWu6///7ceeed2Wqrrca6HACAJOvIFI6FCxdm4403Fp7XMVWVjTfe2F8WAIC1yjoRoJMIz+so7ysAsLZZZwI0AAA8HtaJOdDDTTnu0jXa3/xTDlxtm/Hjx2fq1KlZvHhxtttuu3z2s5/NU5/61BXa7bXXXrnmmmseUz1XXnllJk6cmL322itJcsYZZ+SpT31qjjjiiMfU70gOPfTQnHrqqdl6663z8MMP55hjjsk3v/nNPP3pT88GG2yQD37wg/mjP/qjEc//nnvuyUEHHZQbb7xxWX8nnnhiJk+enL/+67/OX//1X+eAAw7In/7pn67xugEABsUI9BoyadKkzJs3LzfeeGMmTpyYM844Y7ntixcvTpLHHJ6TXoAe2s+b3/zmgYTnm266KUuWLMnWW2+dJHnDG96QZzzjGfnpT3+auXPn5jOf+Uzuu+++JKs//5EcddRROeWUU9Z43QAAgyRAD8A+++yT2267LVdeeWX22WefHHzwwdl+++2TJJMnT17W7oMf/GCmTp2anXfeOccdd1yS5Gc/+1lmzZqV3XbbLfvss09+8pOfLNf3/Pnzc8YZZ+TDH/5wpk2blquuuionnnhiTjvttCTJjBkzcvTRR2f69OnZbrvtct111+V//s//mW222SZ/+7d/u6yfz3/+89ljjz0ybdq0vOlNb8qSJUtWOI9zzz03hxxyyLK6vv/97+fkk0/OuHG9j81WW22VAw9ccXR+6fmvzpZbbpn7778///mf/7natgAAawsBeg1bvHhxLr/88kydOjVJcv311+ejH/1obr311uXaXX755fnqV7+a73//+7nhhhty7LHHJkne+MY35vTTT8/cuXNz2mmn5S//8i+X22/KlCl585vfnKOPPjrz5s3LPvvss0INEydOzJw5c/LmN785hxxySD7+8Y/nxhtvzNlnn537778/N998c84///xcffXVmTdvXsaPH59zzz13hX6uvvrq7Lbbbkl6o9HTpk3L+PHjO53/6uy66665+uqrR9UWAGBtsE7OgR4LjzzySKZNm5akNwL7+te/Ptdcc0322GOPEe9h/M1vfjN//ud/vmye9DOe8Yw8/PDDueaaa/Lyl798Wbvf/e53nWs5+OCDkyRTp07NDjvskGc+85lJkq233jp33HFHvvOd72Tu3LnZfffdl9W+2WabrdDP3XffnU033XRUxxzp/O++++4R2w69s8Zmm22Wu+66a/QnBwAwxgToNWTpHODhnva0p426j9///vd5+tOfPmI/XTzlKU9JkowbN27Z70uXFy9enNZaZs+enQ984AOr7GfSpEnL7sG8ww475IYbbsiSJUtGHIUe6fw33njjPPDAA8ut+/Wvf73cF4qFCxdm0qRJ3U4QAGAMmcIxRvbdd9985jOfyW9/+9skvWC54YYbZquttso///M/J+k9ie+GG25YYd8NNtggDz300B987Be/+MW54IILcs899yw79s9//vMV2m233XbL5jI/97nPzfTp0/Oe97wnrbUkvfnYl1668jueTJ48Oc985jNzxRVXLDvOv/7rv+ZP/uRPlrW59dZbs+OOO/7B5wIA8HhbJ0egR3PbubE2a9aszJs3L9OnT8/EiRNzwAEH5P3vf3/OPffcHHnkkTn55JOzaNGivPKVr8zOO++83L5/9md/lkMPPTRf/epXc/rpp3c+9vbbb5+TTz45L33pS/P73/8+EyZMyMc//vFsueWWy7U78MADc+WVV+YlL3lJkuTTn/50jjnmmDzvec/LpEmTsskmm+Tv/u7vVnmsz33uc3nLW96St7/97UmS97znPXnuc5+bJFm0aFFuu+22TJ8+vfM5AACMlVo6mvhEMX369DZnzpzl1t18883ZbrvtxqiiddcjjzySmTNn5uqrr17txYN/iAsvvDDXX3993ve+962ynfcXABgLVTW3tbbCSJ8pHKzUpEmT8t73vje//OUvB9L/4sWLc8wxxwykbwCAQVknp3Cw5uy3334D63vo3UYAAJ4ojEADAEAHAjQAAHRgCgcAjIGpnx3dE1vhye5Hs3801iWswAg0AAB0sG6OQJ+40Rrub8Fqm4wfPz5Tp07NokWLst566+WII47I0UcfnXHjVv4dZf78+TnooINy4403Zt68ebnrrrtywAEHjLqs+fPn55prrsnhhx+eJJkzZ04+97nP5R/+4R9G3cdofeQjH8kznvGMHHHEEUmS0047LZ/+9Kez/vrrZ8KECTnqqKNyxBFHZMaMGbn77ruz/vrrZ/LkyTnrrLOy7bbbZsqUKZkzZ0422WSTJMmVV16Z0047LZdcckkuueSSXHvttTnppJPWeN0AAGuaEeg1ZOmjrG+66aZ84xvfyOWXX573vve9o95/3rx5ueyyyzodc/78+fnCF76wbHn69OkDCc+LFy/OWWedtSyon3HGGfnGN76Ra6+9NvPmzcu3vvWtDL2f+Lnnnpsbbrghs2fPzjve8Y7V9n/ggQfm4osvXvZURgCAtZkAPQCbbbZZzjzzzHzsYx9Lay1LlizJO97xjuy+++7Zaaed8slPfnK59o8++mje/e535/zzz8+0adNy/vnn59prr82ee+6ZXXbZJXvttVduueWWFY5z3HHH5aqrrsq0adPy4Q9/OFdeeWUOOuigJMmJJ56Y2bNnZ5999smWW26Zr3zlKzn22GMzderUzJo1K4sWLUqSzJ07Ny960Yuy2267Zb/99svdd9+9wnGuuOKK7Lrrrllvvd4fLN7//vfnE5/4RDbccMMkyYYbbpjZs2evsN8LX/jCZY8CX5WqyowZM3LJJZesti0AwFhbN6dwrAW23nrrLFmyJPfcc0+++tWvZqONNsp1112X3/3ud9l7773z0pe+NFWVJJk4cWJOOumkzJkzJx/72MeSJA8++GCuuuqqrLfeevnmN7+Zv/mbv8mXv/zl5Y5xyimnLJsGkfSmRQz1s5/9LN/+9rfz4x//OHvuuWe+/OUv59RTT83LXvayXHrppTnwwANz1FFH5atf/Wo23XTTnH/++XnXu96Vs846a7l+rr766uy2227L6nrooYey9dZbr/Y1uPjiizN16ugukpk+fXquuuqqvOIVrxhVe9Zia3oKFayrtnrOWFcA/IEE6MfB17/+9fzwhz/MBRdckCRZsGBBfvrTn+b5z3/+SvdZsGBBZs+enZ/+9KepqmUjxl3sv//+mTBhQqZOnZolS5Zk1qxZSZKpU6dm/vz5ueWWW3LjjTdm3333TZIsWbIkz3zmM1fo5+677+70KO1XvepVmTRpUqZMmZLTTz89SZZ9WRhq6LrNNtssd911V6fzAwAYCwL0gNx+++0ZP358Nttss7TWcvrpp6/wVL/58+evdP8TTjghM2fOzIUXXpj58+dnxowZnWt4ylOekiQZN25cJkyYsCywjhs3LosXL05rLTvssEO++93vrrKfSZMmZeHChUl60zUmT56c22+/faWj0Oeee26mT1/+sfEbb7xxHnjggWUXEf76179e9nuSLFy4MJMmTep8jgAAjzdzoAfg3nvvzZvf/Oa89a1vTVVlv/32yyc+8Yllo8i33nprfvOb3yy3zwYbbJCHHnpo2fKCBQuy+eabJ0nOPvvsEY8zfJ+utt1229x7773LAvSiRYty0003rdBuu+22W24u8/HHH5+3vOUtefDBB5MkDz/8cD73uc+t8lgzZszIOeeck6Q30v35z38+M2fOXLb91ltvzY477vgHnwsAwONl3RyBHsVt59a0Rx55JNOmTVt2G7vXvOY1efvb354kecMb3pD58+dn1113TWstm266af7lX/5luf1nzpyZU045JdOmTcvxxx+fY489NrNnz87JJ5+cAw88cMRj7rTTThk/fnx23nnnvPa1r80uu+zSqeaJEyfmggsuyNve9rYsWLAgixcvzl/91V9lhx12WK7d/vvvn9e85jXLlo888sg8/PDD2X333TNhwoRMmDAhxxxzzCqPdcIJJ+TII4/MzjvvnNZaZs2alVe/+tXLtn/729/OBz7wgU71AwCMhRp6+7EngunTp7c5c+Yst+7mm2/uNEeX7l72spfl1FNPzTbbbLPG+/7Vr36Vww8/PN/61rdG3O79fYJxESGMylQXEcKojOWTCKtqbmtt+vD1pnAwKqeccsqIt7hbE37xi1/k7//+7wfSNwDAmrZuTuFgjdt2222z7bbbDqTv3XfffSD9AgAMghFoAADoQIAGAIAOBGgAAOhAgF4DZs6cma997WvLrfvIRz6SI488ciDHmz9/fr7whS8sW54zZ07e9ra3DeRYH/nIR5a7x/Npp52WF7zgBZk2bVp23333ZdtmzJiRbbfdNjvvvHP23nvv3HLLLUmSKVOm5L777lu2/5VXXpmDDjooSXLJJZfk3e9+90DqBgAYlHXyIsKpn526Rvtb3e1TDjvssJx33nnLPWnwvPPOy6mnnrpG61hqaYA+/PDDkyTTp09f4cl/a8LixYtz1lln5frrr0+SnHHGGfnGN76Ra6+9NhtuuGEefPDBXHjhhcvaL30C4Zlnnpl3vOMdueiii1bZ/4EHHpgTTjghxx13XJ761Keu8foBAAbBCPQacOihh+bSSy/No48+mqQXcO+6667ss88++frXv54999wzu+66a17+8pfn4YcfTpJcdtllecELXpDddtstb3vb25aNyl577bXZc889s8suu2SvvfZaNpI71HHHHZerrroq06ZNy4c//OHlRnVPPPHEzJ49O/vss0+23HLLfOUrX8mxxx6bqVOnZtasWcuehjh37ty86EUvym677Zb99ttvxFvUXXHFFdl1112z3nq971nvf//784lPfCIbbrhhkt5jvWfPnr3Cfi984QuXe3LhylRVZsyYkUsuuWS1bQEA1hYC9BrwjGc8I3vssUcuv/zyJL3R51e84hW5//77c/LJJ+eb3/xmrr/++kyfPj0f+tCHsnDhwrzpTW/K5Zdfnrlz5+bee+9d1tcLXvCCXHXVVfnBD36Qk046KX/zN3+zwvFOOeWU7LPPPpk3b16OPvroFbb/7Gc/yxVXXJGLLroor371qzNz5sz86Ec/yqRJk3LppZdm0aJFOeqoo3LBBRdk7ty5ed3rXpd3vetdK/Rz9dVXZ7fddkuSPPjgg3nooYey9dZbr/b1uPjiizN16uj+CjB9+vRcddVVo2oLALA2WCencIyFpdM4DjnkkJx33nn5p3/6p3zve9/Lj3/84+y9995JkkcffTR77rlnfvKTn2TrrbfOVltttWzfM888M0myYMGCzJ49Oz/96U9TVctGjLvYf//9M2HChEydOjVLlizJrFmzkiRTp07N/Pnzc8stt+TGG2/MvvvumyRZsmRJnvnMZ67Qz913393pCYCvetWrMmnSpEyZMiWnn356kt4o83BD12222Wa56667Op0fAMBYEqDXkEMOOSRHH310rr/++vz2t7/Nbrvtlosvvjj77rtvvvjFLy7Xdt68eSvt54QTTsjMmTNz4YUXZv78+ZkxY0bnWp7ylKckScaNG5cJEyYsC6zjxo3L4sWL01rLDjvskO9+97ur7GfSpElZuHBhkt50jcmTJ+f2229f6Sj00jnQQ2288cZ54IEHsskmmyRJfv3rXy/7PUkWLlyYSZMmdT5HAICxYgrHGjJ58uTMnDkzr3vd63LYYYclSf74j/84V1999bL5wL/5zW9y6623Ztttt83tt9+e+fPnJ0nOP//8Zf0sWLAgm2++eZLk7LPPHvFYG2ywQR566KE/uNZtt902995777IAvWjRotx0000rtNtuu+2Wm8t8/PHH5y1veUsefPDBJMnDDz+83B06RjJjxoycc845SXoj3Z///Oczc+bMZdtvvfXW7Ljjjn/wuQAAPN4E6DXosMMOyw033LAsQG+66aY5++yzc9hhh2WnnXZaNn1j0qRJ+cd//MfMmjUru+22WzbYYINstNFGSZJjjz02xx9/fHbZZZcsXrx4xOPstNNOGT9+fHbeeed8+MMf7lznxIkTc8EFF+Sd73xndt5550ybNi3XXHPNCu3233///Pu///uy5SOPPDIzZ87M7rvvnh133DH77LNPxo1b9UfohBNOyG233Zadd945u+yyS573vOfl1a9+9bLt3/72t3PggQd2PgcAgLFSrbWxrqGT6dOntzlz5iy37uabb+40V3dt8PDDD2fy5MlpreUtb3lLttlmmxEvCBxrL3vZy3Lqqadmm222WeN9/+pXv8rhhx+eb33rW6ts90R8f5/UTtxorCuAJ4SpWz1nrEuAJ4TV3U54kKpqbmtthXsFG4EeI5/61Kcybdq07LDDDlmwYEHe9KY3jXVJIzrllFNGvMXdmvCLX/wif//3fz+QvgEABsVFhGPk6KOPXitHnIfbdttts+222w6k7913330g/QIADJIRaAAA6GCdCdBPtLncjI73FQBY26wTAXr99dfP/fffL2ytY1pruf/++7P++uuPdSkAAMusE3Ogt9hii9x5553LPRKbdcP666+fLbbYYqzLAABYZp0I0BMmTFj2WGwAABikdWIKBwAAPF4EaAAA6ECABgCADgRoAADoQIAGAIAOBGgAAOhAgAYAgA4EaAAA6ECABgCADgRoAADoQIAGAIAOBGgA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\n",
            "text/plain": [
              "\u003cFigure size 864x648 with 1 Axes\u003e"
            ]
          },
          "metadata": {
            "needs_background": "light",
            "tags": []
          },
          "output_type": "display_data"
        }
      ],
      "source": [
        "#@title CPU greeks computation speed\n",
        "\n",
        "ind = np.arange(1)  # the x locations for the groups\n",
        "width = 0.35  # the width of the bars\n",
        "\n",
        "fig, ax = plt.subplots()\n",
        "\n",
        "fig.set_figheight(9)\n",
        "fig.set_figwidth(12)\n",
        "\n",
        "ax.bar(ind - width/8, [time_price_cpu], width / 8,\n",
        "       label='Price time (CPU)')\n",
        "ax.bar(ind, [time_delta_cpu], width / 8,\n",
        "       label='Delta time (CPU)')\n",
        "ax.bar(ind + width/8, [time_vega_cpu], width / 8,\n",
        "       label='Vega time (CPU)')\n",
        "\n",
        "# Add some text for labels, title and custom x-axis tick labels, etc.\n",
        "ax.set_ylabel('Time (sec)')\n",
        "ax.set_title('Monte Carlo. Greeks computation on CPU')\n",
        "ax.set_xticks(ind)\n",
        "ax.legend()\n",
        "\n",
        "\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 553
        },
        "executionInfo": {
          "elapsed": 380,
          "status": "ok",
          "timestamp": 1629282354860,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "Kjw6GvTmPdkx",
        "outputId": "8b62f6b5-57c5-4d65-d95f-fbd6c9968383"
      },
      "outputs": [
        {
          "data": {
            "image/png": "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DmXhwoU88MADDB06lEMPPZT+/fsD0KlTp/p2F1xwAdXV1QwcOJBJk0rXnXjhhRcYMWIEgwcPZujQoTz77LNr9V1bW8tVV13FxRdfzKBBg5g5cyaTJ0/moosuAuBLX/oSEydOpKamhn79+jF79mz++Z//mT59+vCDH/ygvp+bb76ZffbZh0GDBnH88cezevXqdZ7HL37xC0aPHl1f1yOPPMKUKVNo06YUX3v06MFpp522zn5t27ZlyJAhLFy4sFmv1e9///v6Lx85VDJczwb6RETviGgPHA7MKGszAxhXPB4D3J9SShHRozghkojYBegDvFjBWiVJklq9uro67rnnHqqrqwF49NFHufTSS3nuuefWanfPPffwm9/8hocffpjHH3+cU089FYDjjjuOH//4x8ydO5eLLrqI73znO2vt16tXL0444QQmTpzIvHnzGDp06Do1tG/fnjlz5nDCCScwevRorrjiCp566imuv/56lixZwjPPPMO0adOYNWsW8+bNo6qqil/84hfr9DNr1iwGDx4MlEaxBw4cWB+s1+eDDz7gv//7v+tfg/Vp06YNu+66K48//vgG2zZXxeZcF3OoTwTuBaqA61JK8yPibGBOSmkGcC1wU0QsBN6iFMABDgDOjohVwEfACSmlt9Y9iiRJkpYvX86gQaWLrA0dOpRjjz2WBx98kH322afRazT//ve/55hjjqmfl73NNtuwbNkyHnzwQQ477LD6dh9++OFG13LooYcCUF1dze677872228PwC677MIrr7zCn//8Z+bOncvee+9dX3vPnj3X6ef111+nqWm/5557Lr/61a944403eO2114DS6PagQYOICEaPHs3IkSP54x//2Oj+Da+f0bNnT1577bX6IL+pKnoTmZTS3cDdZevObPB4BXBYI/v9J/CflaxNkiRpS7FmznW5z3zmM83u46OPPqJbt26N9rMxOnToAJRGhdc8XrNcV1dHSolx48bxox/9aL39dOzYsf4a0/379+fxxx/no48+ok2bNpxxxhmcccYZa01zWTPnuqHu3bvz9ttvr7XurbfeYtttt61fXrFiBR07dvx4T7YRm+sJjZIkSaqQgw8+mJ///Od88MEHQClwdunShd69e/OrX/0KKN2hsLHpEp07d+a999772Mc+6KCDmD59Om+88Ub9sV9++eV12vXr169+3vSuu+5KTU0NP/jBD+rnZ69YsYKU0nqP1adPH1577TWeeeYZAF5++WUef/zx+lF+gOeee4499tjjYz+fct7+XJIkKaPmXDqvpY0YMYJ58+ZRU1ND+/bt+cpXvsJ5553HL37xCyZMmMCUKVNYtWoVhx9+OAMHDlxr36997WuMGTOG3/zmN/z4xz/e6GP379+fKVOmcMghh/DRRx/Rrl07rrjiCnbeeee12n31q1/lgQce4Mtf/jIA11xzDaeccgq77ror3bt3p2PHjhu80keHDh24+eabOeaYY1ixYgXt2rXjmmuuoWvXrgD87W9/o2PHjmy33XYb/TyaEhtK/K1FTU1NmjNnTkuXIUmSPmWeeeYZ+vXr19JlbHGWL1/OsGHDmDVrFlVVVRU5xsUXX0yXLl049thjm2zT2PsbEXNTSjWNtXdaiCRJkjY7HTt25KyzzuLVV8uv5JxPt27dGDdu3IYbbgSnhUiSJGmzNHz48Ir2f8wxx2Tv05FrSZIkKRPDtSRJkpSJ00IkfXImd23pCqTWYfLSlq5A0sfkyLUkSZKUiSPXkiRJOeX+la4Zv2RUVVVRXV3NqlWraNu2LUcddRQTJ06kTZumx1Fra2sZNWoUTz31FPPmzeO1117jK1/5SrPLqq2t5cEHH+SII44AYM6cOdx4441cdtllze6juS655BK22WYbjjrqKACmTp3K1VdfTbt27WjTpg0HHXQQF1xwAe3ataNXr1507tyZiGC77bbjxhtvZLvttqNTp04sW7asvs/rr7+eOXPmcPnll3P55Zez9dZbM378+E2u1ZFrSZKkVm7N7c/nz5/P7373O+655x7OOuusZu8/b9487r777o06Zm1tLbfcckv9ck1NTUWCdV1dHdddd119iL/qqqu47777eOihh3jyySeZPXs2PXv2ZPny5fX7/OEPf+CJJ56gpqaG8847b4PHGD9+/Me6IU5jDNeSJElbkJ49e3L11Vdz+eWXk1Ji9erVnHLKKey9994MGDCAn/70p2u1X7lyJWeeeSbTpk1j0KBBTJs2jUceeYT99tuPPffckyFDhrBgwYJ1jjNp0iRmzpzJoEGDuPjii3nggQcYNWoUAJMnT2bcuHEMHTqUnXfemdtvv51TTz2V6upqRowYwapVqwCYO3cuBx54IIMHD2b48OG8/vrr6xzn/vvvZ6+99qJt29KEi3PPPZcrr7ySbt26AdC+fXsmTZpEly5d1tn3gAMOqL+F+vpsvfXW9OrVi0ceeWSDbTfEcC1JkrSF2WWXXVi9ejVvvPEG1157LV27dmX27NnMnj2bn/3sZ7z00kv1bdu3b8/ZZ5/NN77xDebNm8c3vvENdtttN2bOnMljjz3G2Wefzfe///11jnH++eczdOhQ5s2bx8SJE9fZ/sILL3D//fczY8YMvvnNbzJs2DCefPJJOnbsyF133cWqVav47ne/y/Tp05k7dy7jx4/njDPOWKefWbNmMXjwYADeffddli1bRu/evZv1Otx5551UV1c3q21NTQ0zZ85sVtv1cc61JEnSFuy+++7jiSeeYPr06QAsXbqU559/ni984QtN7rN06VLGjRvH888/T0TUjzRvjJEjR9KuXTuqq6tZvXo1I0aMAKC6upra2loWLFjAU089xcEHHwzA6tWr2X777dfp5/XXX2/y9vL33nsvp512Gu+88w633HILQ4YMAWDYsGFUVVUxYMAApkyZ0mSNEVH/uGfPnjz77LMb/TzLGa4lSZK2MC+++CJVVVX07NmTlBI//vGP17nbYW1tbZP7//CHP2TYsGHccccd1NbW8qUvfWmja+jQoQMAbdq0oV27dvVBtk2bNtTV1ZFSYvfdd+cvf/nLevvp2LEjK1asAKBLly506tSJl156id69ezN8+HCGDx/OqFGjWLlyZf0+f/jDH9h2223X6WflypW0b98egLfeemutNitWrKBjx44b/TzLOS1EkiRpC7J48WJOOOEETjzxRCKC4cOHc+WVV9aPPj/33HO8//77a+3TuXNn3nvvvfrlpUuXssMOOwClq2o0pnyfjdW3b18WL15cH65XrVrF/Pnz12nXr1+/teZNn3766UyYMIF33nkHgJRSffhenwMPPJCbb74ZgOXLl3PbbbcxbNiw+u3PPfcce+yxx8d+Pms4ci1JkpRTC9wEaPny5QwaNKj+UnxHHnkkJ598MgDf+ta3qK2tZa+99iKlRI8ePfj1r3+91v7Dhg3j/PPPZ9CgQZx++umceuqpjBs3jilTpvDVr3610WMOGDCAqqoqBg4cyNFHH82ee+65UTW3b9+e6dOnc9JJJ7F06VLq6ur43ve+x+67775Wu5EjR3LkkUfWL0+YMIH333+ffffdlw4dOtCpUyf233//DR7/0ksv5fjjj+eyyy4jpcRRRx3FAQccUL991qxZTJ48eaOeQ2MipbTJnWwOampq0pw5c1q6DEnr4x0apebxDo2tyjPPPNPknGDl8fWvf50LL7yQPn36VKT/xx57jKlTp3LTTTets62x9zci5qaUahrry2khkiRJ2qydf/75jV6mL5c333yTc845J0tfTguRJEnSZq1v37707du3Yv2vuWJJDo5cS5IkSZkYriVJkqRMDNeSJElSJoZrSZKkVmzYsGHce++9a6275JJLmDBhQkWOV1tbyy233FK/PGfOHE466aSKHOuSSy7hxhtvrF+eOnUqu+22G9XV1QwcOJCTTz65/vrdvXr1orq6mgEDBnDIIYfwP//zPwB06tRprT6vv/56TjzxRAAuv/xyrrvuuqw1e0KjJElSRtU3VGft78lxT653+9ixY7n11lvXugPjrbfeyoUXXpi1jjXWhOsjjjgCgJqaGmpqGr0q3Sapq6vjuuuu49FHHwXgqquu4r777uOhhx6iW7durFy5kqlTp7J8+XLatWsH/O+dGb///e9z3nnncdlll633GOPHj2f//fdn/Pjx2ep25FqSJKkVGzNmDHfddVf97b9ra2t57bXXGDp0KPfddx/77bcfe+21F4cddhjLli0D4O6772a33XZj8ODBnHTSSYwaNQqARx55hP32248999yTIUOGsGDBgnWON2nSJGbOnMmgQYO4+OKLeeCBB+r3nzx5MuPGjWPo0KHsvPPO3H777Zx66qlUV1czYsSI+lHmuXPncuCBBzJ48GCGDx/e6GX27r//fvbaay/ati2NBZ977rlceeWVdOvWDSjdhGbSpEl06dJlnX0POOCAte7q2JStt96aXr168cgjj2ywbXMZriVJklqxbbbZhn322Yd77rkHKI1a/+u//itLlixhypQp/P73v+fRRx+lpqaGqVOnsmLFCo4//njuuece5s6dy+LFi+v72m233Zg5cyaPPfYYZ599Nt///vfXOd7555/P0KFDmTdvHhMnTlxn+wsvvMD999/PjBkz+OY3v8mwYcN48skn6dixI3fddRerVq3iu9/9LtOnT2fu3LmMHz+eM844Y51+Zs2axeDBgwF49913WbZsGb17927Wa3LnnXdSXd28XxBqamqYOXNms9o2h9NCJEmSWrk1U0NGjx7NrbfeyrXXXstDDz3E008/zf777w/AypUr2W+//Xj22WfZZZdd6oPq2LFjufrqqwFYunQp48aN4/nnnyci6keaN8bIkSNp164d1dXVrF69mhEjRgBQXV1NbW0tCxYs4Kmnnqq/tvTq1avZfvvt1+nn9ddfb/LOl/feey+nnXYa77zzDrfccgtDhgwBSvPPq6qqGDBgAFOmTGmyxoiof9yzZ0+effbZjX6eTTFcS5IktXKjR49m4sSJPProo3zwwQcMHjyY3/72txx88MH88pe/XKvtvHnzmuznhz/8IcOGDeOOO+6gtraWL33pSxtdS4cOHQBo06YN7dq1qw+ybdq0oa6ujpQSu+++O3/5y1/W20/Hjh1ZsWIFAF26dKFTp0689NJL9O7dm+HDhzN8+HBGjRpVPx0G/nfOdXk/K1eupH379gC89dZba7VZsWIFHTt23Ojn2RSnhUiSJLVynTp1YtiwYYwfP56xY8cC8MUvfpFZs2bVzz1+//33ee655+jbty8vvvgitbW1AEybNq2+n6VLl7LDDjsApatqNKZz58689957H7vWvn37snjx4vpwvWrVKubPn79Ou379+q01b/r0009nwoQJvPPOOwCklOrD9/oceOCB3HzzzQAsX76c2267jWHDhtVvf+6559hjjz0+9vMpZ7iWJEnaAowdO5bHH3+8Plz36NGD66+/nrFjxzJgwID6KSEdO3bkJz/5CSNGjGDw4MF07tyZrl27AnDqqady+umns+eee1JXV9focQYMGEBVVRUDBw7k4osv3ug627dvz/Tp0znttNMYOHAggwYN4sEHH1yn3ciRI/nTn/5UvzxhwgQOOugg9t13XwYMGMD+++/PnnvuyZ577rne41166aXcfvvtDBo0iC9+8YscdthhHHDAAfXbZ82alfX255FSytZZS6qpqUlz5sxp6TIkrc/kri1dgdQ6TF7a0hVoIzzzzDNNzg3eXC1btoxOnTqRUuLf//3f6dOnT6MnJ7a0r3/961x44YX06dOnIv0/9thjTJ06lZtuuqnJNo29vxExN6XU6PUHHbmWJEn6lPnZz37GoEGD2H333Vm6dCnHH398S5fUqPPPP7/Ry/Tl8uabb3LOOedk7dMTGiVJkj5lJk6cuFmOVJfr27cvffv2rVj/OaeDrOHItSRJkpSJ4VqSJGkTbSnnsGltH+d9NVxLkiRtgq222oolS5YYsLcwKSWWLFnCVltttVH7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\n",
            "text/plain": [
              "\u003cFigure size 864x648 with 1 Axes\u003e"
            ]
          },
          "metadata": {
            "needs_background": "light",
            "tags": []
          },
          "output_type": "display_data"
        }
      ],
      "source": [
        "#@title GPU greeks computation speed\n",
        "\n",
        "ind = np.arange(1)  # the x locations for the groups\n",
        "width = 0.35  # the width of the bars\n",
        "\n",
        "fig, ax = plt.subplots()\n",
        "\n",
        "fig.set_figheight(9)\n",
        "fig.set_figwidth(12)\n",
        "\n",
        "ax.bar(ind - width/8, [time_price_gpu], width / 8,\n",
        "       label='Price time (GPU)')\n",
        "ax.bar(ind, [time_delta_gpu], width / 8,\n",
        "       label='Delta time (GPU)')\n",
        "ax.bar(ind + width/8, [time_vega_gpu], width / 8,\n",
        "       label='Vega time (GPU)')\n",
        "\n",
        "# Add some text for labels, title and custom x-axis tick labels, etc.\n",
        "ax.set_ylabel('Time (sec)')\n",
        "ax.set_title('Monte Carlo. Greeks computation on GPU')\n",
        "ax.set_xticks(ind)\n",
        "ax.legend()\n",
        "\n",
        "\n",
        "plt.show()"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "collapsed_sections": [],
      "last_runtime": {
        "build_target": "",
        "kind": "local"
      },
      "name": "Monte_Carlo_Euler_Scheme.ipynb",
      "provenance": [
        {
          "file_id": "1rT50234_4wb5VFNWyheBfyVlV-iuRA0t",
          "timestamp": 1553699488325
        },
        {
          "file_id": "1G2Wm6NzpdH3gPFsFTaLslgonTTkI0U0h",
          "timestamp": 1552493606270
        }
      ],
      "toc_visible": true
    },
    "environment": {
      "name": "tf2-gpu.2-1.m47",
      "type": "gcloud",
      "uri": "gcr.io/deeplearning-platform-release/tf2-gpu.2-1:m47"
    },
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.7.6"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
